<?php
/**
 * [LupMisNotAllowedSync]
 * 本代码为系统自动生成代码，请根据自己业务进行修改;
 * 生成时间 2022-02-01 22:12:12;
 * 版权所有 2020-2022 lizhongwen，并保留所有权利;
 * 说明: 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用；不允许对程序代码以任何形式任何目的的再发布;
 * 作者: 中文Lee;
 * 作者主页: http://www.lizhongwen.com;
 * 邮箱: 360811363@qq.com;
 * QQ: 360811363;
 */

namespace application\core;


use ReflectionException;
use ReflectionMethod;

class Lib
{

    /*
     * 将下划线命名数组转换为驼峰式命名数组
     * @pram $arr 原数组
     * @pram bool $ucFirst 首字母大小写，false 小写，TRUE 大写
     */
    public static function camelCase($arr, bool $ucFirst = false)
    {
        if (!is_array($arr)) {   //如果非数组原样返回
            return $arr;
        }
        $temp = [];
        foreach ($arr as $key => $value) {
            $key1 = self::convertUnderline($key, $ucFirst);
            $value1 = self::camelCase($value);
            $temp[$key1] = $value1;
        }
        return $temp;
    }


    /**
     * 将下划线命名转换为驼峰式命名
     * @param $str
     * @param bool $ucFirst
     * @return array|string|string[]
     */
    public static function convertUnderline($str, bool $ucFirst = true)
    {
        $str = ucwords(str_replace('_', ' ', $str));
        $str = str_replace(' ', '', lcfirst($str));
        return $ucFirst ? ucfirst($str) : $str;
    }

    /**
     * 或者方法的注释
     * @param $clsName
     * @param $function
     * @param $tagName
     * @return string
     */
    public static function getFunctionAnnotation($clsName, $function, $tagName): string
    {
        $tagName = $tagName . " ";
        $match = [];
        $annotation = "";
        $ret = "";
        if (method_exists($clsName, $function)) {
            try {
                $func = new ReflectionMethod($clsName, $function);
                $annotation = $func->getDocComment();
            } catch (ReflectionException $e) {
            }
            if ($annotation) {
                if (strpos($annotation, $tagName)) {
                    preg_match('/' . $tagName . '(.*?)\n/', $annotation, $match);
                }
            }
            if ($match[1]) {
                $ret = trim($match[1]);
            }
        }
        return $ret;
    }


    /**
     * 扫描文件夹根据更新时间
     * @param $path
     * @param false $desc
     * @return array|false
     */
    public static function scanDirByUpdateDate($path, bool $desc = false)
    {
        $file_list = scandir($path);
        if (!is_array($file_list)) { // 404 或不是目录，中断操作，继承原方法。
            return $file_list;
        }
        foreach ($file_list as $f_name) {
            if ($f_name != "." && $f_name != "..") { // 跳过两个特殊的目录
                $fct = filectime($path . "/" . $f_name);
                $new_list[$f_name] = $fct; // 为什么要用fct作为value 的格式，是因为时间可能会重合如果作为key就会覆写，而文件名是不会重复的
            }
        }
        if (empty($new_list)) {
            return [];
        }
        // 排序
        if ($desc) { // 反排
            arsort($new_list);
        } else {
            asort($new_list);
        }
        // 输出排序后的新清单
        $file_list = [];
        foreach ($new_list as $fn => $ts) { // 时间戳其实已经没有用了，只要按重排的顺序取出key
            unset($ts);
            $file_list[] = $fn;
        }
        return $file_list;
    }

    /**
     * 全角转半角
     * @param $str
     * @return string
     */
    public static function makeSemiangle($str): string
    {
        $arr = ['０' => '0', '１' => '1', '２' => '2', '３' => '3', '４' => '4', '５' => '5', '６' => '6', '７' => '7', '８' => '8', '９' => '9', 'Ａ' => 'A', 'Ｂ' => 'B', 'Ｃ' => 'C', 'Ｄ' => 'D', 'Ｅ' => 'E', 'Ｆ' => 'F', 'Ｇ' => 'G', 'Ｈ' => 'H', 'Ｉ' => 'I', 'Ｊ' => 'J', 'Ｋ' => 'K', 'Ｌ' => 'L', 'Ｍ' => 'M', 'Ｎ' => 'N', 'Ｏ' => 'O', 'Ｐ' => 'P', 'Ｑ' => 'Q', 'Ｒ' => 'R', 'Ｓ' => 'S', 'Ｔ' => 'T', 'Ｕ' => 'U', 'Ｖ' => 'V', 'Ｗ' => 'W', 'Ｘ' => 'X', 'Ｙ' => 'Y', 'Ｚ' => 'Z', 'ａ' => 'a', 'ｂ' => 'b', 'ｃ' => 'c', 'ｄ' => 'd', 'ｅ' => 'e', 'ｆ' => 'f', 'ｇ' => 'g', 'ｈ' => 'h', 'ｉ' => 'i', 'ｊ' => 'j', 'ｋ' => 'k', 'ｌ' => 'l', 'ｍ' => 'm', 'ｎ' => 'n', 'ｏ' => 'o', 'ｐ' => 'p', 'ｑ' => 'q', 'ｒ' => 'r', 'ｓ' => 's', 'ｔ' => 't', 'ｕ' => 'u', 'ｖ' => 'v', 'ｗ' => 'w', 'ｘ' => 'x', 'ｙ' => 'y', 'ｚ' => 'z'
        ];

        return strtr($str, $arr);
    }

    /**
     * 获取uuid
     * @return string
     */
    public static function generateUUIDv4(): string
    {
        return md5(uniqid(mt_rand() . self::createOrderNo() . self::serialGen(), true));

    }

    public static function uuid(): string
    {
        mt_srand((double)microtime() * 10000); // optional for php 4.2.0 and up.
        $char_id = strtolower(md5(uniqid(rand(), true)));
        $hyphen = chr(45); // "-"
        return substr($char_id, 0, 8) . $hyphen . substr($char_id, 8, 4) . $hyphen . substr($char_id, 12, 4) . $hyphen . substr($char_id, 16, 4) . $hyphen . substr($char_id, 20, 12);
    }

    /**
     * 将$filtering的图像文件变成等比不变形的缩略图($width*$dstheight)
     * $type 用1表示留白方式处理，2表示截取方式处理
     */
    public static function setImgThumb($file_string, $savePath, $dst_width, $dstheight, $type, $q)
    {
        //list ($src_w, $src_h, $filetype) = getimagesize($file_string);
        $source_info = getimagesize($file_string);
        $src_w = $source_info[0];
        $src_h = $source_info[1];
        $filetype = $source_info['mime'];
        // $source_ratio = $source_height / $source_width;
        // $target_ratio = $target_height / $target_width;
        if ($dst_width == 0) {
            $dst_width = $src_w;
        }
        if ($dstheight == 0) {
            $dstheight = $src_h;
        }
        // 0.局部变量声明
        $srcimg = 0;
        $dstx = $dsty = $srcx = $srcy = 0;
        $dstimg = imagecreatetruecolor($dst_width, $dstheight);
        // 1.得到$filtering里的文件类型（png,gif,jpeg,jpg）
        switch ($filetype) {
            case 'image/png':
                $alpha = imagecolorallocatealpha($dstimg, 0, 0, 0, 127);
                imagefill($dstimg, 0, 0, $alpha);

                $srcimg = imagecreatefrompng($file_string);
                break;
            case 'image/jpeg':
                $alpha = imagecolorAllocate($dstimg, 255, 255, 255);
                imagefill($dstimg, 0, 0, $alpha);
                $srcimg = imagecreatefromjpeg($file_string);
                break;
            case 'image/gif':
                $srcimg = imagecreatefromgif($file_string);
                break;
        }
        // 2.得到原来文件的宽和高$src_w,$src_h
        // 3.计算变形
        if ($dst_width / $dstheight < $src_w / $src_h) { // 传来的图像是横图
            if ($type == 1) { // 用户需要留白处理
                $dsty = intval(($dstheight - $src_h * $dst_width / $src_w) / 2);
                $dstheight = $dstheight - 2 * $dsty;
            } else { // 用户需要截取处理
                $srcx = intval($src_w - $dst_width * $src_h / $dstheight) / 2;
                $src_w = $src_w - 2 * $srcx;
            }
        } else { // 传来的图像是竖图或等比的图
            if ($type == 1) { // 用户需要留白处理
                $dstx = intval(($dst_width - $src_w * $dstheight / $src_h) / 2);
                $dst_width = $dst_width - 2 * $dstx;
            } else // 用户需要截取处理
            {
                $srcy = intval(($src_h - $dstheight * $src_w / $dst_width) / 2);
                $src_h = $src_h - 2 * $srcy;
            }
        }
        // 4.绘制缩略图
        imagecopyresampled($dstimg, $srcimg, $dstx, $dsty, $srcx, $srcy, $dst_width, $dstheight, $src_w, $src_h);
        switch ($filetype) {
            case 'image/png':
                imagesavealpha($dstimg, true);
                if (!empty($savePath)) {
                    imagepng($dstimg, $savePath);
                } else {
                    header('Content-Type: image/png');
                    imagepng($dstimg, null, $q);
                }
                break;
            case 'image/jpeg':
                if (!empty($savePath)) {
                    imagejpeg($dstimg, $savePath, $q);
                } else {
                    header('Content-Type: image/jpeg');
                    imagejpeg($dstimg, null, $q);
                }
                break;
            case 'image/gif':
                if (!empty($savePath)) {
                    imagegif($dstimg, $savePath);
                } else {
                    header('Content-Type: image/gif');
                    imagegif($dstimg);
                }
                break;
        }
        imagedestroy($dstimg);
        imagedestroy($srcimg);
        return $dstimg;
    }

    /**
     * 根据生日获取年龄
     * @param $birthday
     * @return false|int|mixed|string
     */
    public static function getAgeByBirthday($birthday)
    {
        list ($year, $month, $day) = explode("-", $birthday);
        $year_diff = date("Y") - $year;
        $month_diff = date("m") - $month;
        $day_diff = date("d") - $day;
        if ($day_diff < 0 || $month_diff < 0)
            $year_diff--;
        return $year_diff;
    }

    /**
     * 从数组查找最小值或者最大值
     * @param array $arr
     * @param string $field
     * @param string $type
     * @return false|mixed
     */
    public static function searchMinMax(array $arr, string $field, string $type = 'min') // 最小值 只需要最后一个max函数 替换为 min函数即可
    {
        if (!is_array($arr) || !$field) { // 判断是否是数组以及传过来的字段是否是空
            return false;
        }
        $temp = array();
        foreach ($arr as $val) {
            $temp[] = $val[$field]; // 用一个空数组来承接字段
        }
        return $type($temp); // 用php自带函数 max 来返回该数组的最大值，一维数组可直接用max函数
    }

    /**
     * 根据秘钥签名获取
     * @param array $param
     * @param string $secretKey
     * @return string
     */
    public static function getSign(array $param, string $secretKey): string
    {
        unset($param['sign']);
        ksort($param);
        $sign_str = '';
        if (is_array($param)) {
            foreach ($param as $key => $value) {
                if (!empty($value)) {
                    $sign_str .= $key . '=' . $value . '&';
                }
            }
            $sign_str = rtrim($sign_str, '&');
        }
        $sign_str .= "&secretKey=" . $secretKey;
        return md5($sign_str);
    }

    public static function getCurl($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        $data = "";
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $tmpInfo = curl_exec($ch);
        if (curl_errno($ch)) {
            return curl_error($ch);
        }
        curl_close($ch);
        return json_decode($tmpInfo, true);
    }

    /**
     * 加载文件
     *
     * @param  $file
     * @return mixed
     */
    public static function loadFile($file)
    {
        $file = sprintf("%s" . $file, APP_PATH);
        return include "$file";
    }

    public static function xssFilter($html): string
    {
        $ms = [];
        preg_match_all("/\<([^\<]+)\>/is", $html, $ms);
        $searchArr[] = '<';
        $replaceArr[] = '&lt;';
        $searchArr[] = '>';
        $replaceArr[] = '&gt;';
        if ($ms[1]) {
//             $allowTags = 'img|a|font|div|table|tbody|caption|tr|td|th|br|p|b|strong|i|u|em|span|ol|ul|li|blockquote|video|audio|h1|h2|h3|h4|h5|h6|hr|pre|br|view|text|dd|dl|dt|iframe|small|q|';
            $ms[1] = array_unique($ms[1]);
            foreach ($ms[1] as $value) {
                $searchArr[] = "&lt;" . $value . "&gt;";
                $value = str_replace('&amp;', '_lup_tmp_str_', $value);
                $value = htmlspecialchars($value, ENT_NOQUOTES);
                $value = str_replace('_lup_tmp_str_', '&amp;', $value);
                $value = str_replace(array('\\', '/*'), array('.', '/.'), $value);
                $skipKeys = ['onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate',
                    'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange',
                    'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick',
                    'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate',
                    'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete',
                    'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
                    'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart',
                    'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop',
                    'onsubmit', 'onunload', 'javascript', 'script', 'eval', 'behaviour', 'expression','ontouchstart','ontouchend'];
                $skipKeys = implode('|', $skipKeys);
                $value = trim(preg_replace(["/($skipKeys)/i"], "lup-del_$1", $value), '_');
//                $value = preg_replace(["/($skipKeys)/i"], 'lup-del', $value);
                //这里我么就不开启了，上面我们已经替换了危险方法
//                if (!preg_match("/^[\/|\s]?($allowTags)(\s+|$)/is", $value)) {
//                    $value = '';
//                }
                $replaceArr[] = empty($value) ? '' : "<" . str_replace('&quot;', '"', $value) . ">";
            }
        }
        return str_replace($searchArr, $replaceArr, $html);
    }

    /*
     * 处理Excel导出
     * @param $datas array 设置表格数据
     * @param $titlename string 设置head
     * @param $title string 设置表头
     */
    public function excelData($datas, $titlename, $title, $filename)
    {
        $str = "<html xmlns:o=\"urn:schemas-microsoft-common:office:office\"\r\nxmlns:x=\"urn:schemas-microsoft-common:office:excel\"\r\nxmlns=\"http://www.w3.org/TR/REC-html40\" lang=\"\">\r\n<head>\r\n<meta http-equiv=Content-Type content=\"text/html; charset=utf-8\">\r\n</head>\r\n<body>";
        $str .= "<table border='1'><head>" . $titlename . "</head>";
        $str .= $title;
        foreach ($datas as $rt) {
            $str .= "<tr>";
            foreach ($rt as $v) {
                $str .= "<td>$v</td>";
            }
            $str .= "</tr>\n";
        }
        $str .= "</table></body></html>";
        header("Content-Type: application/vnd.ms-excel; name='excel'");
        header("Content-type: application/octet-stream");
        header("Content-Disposition: attachment; filename=" . $filename);
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Pragma: no-cache");
        header("Expires: 0");
        exit($str);
    }

    /**
     * 导出大数据，先保存到服务器，然后下载
     * @param string $filename
     * @param array $data
     * @param string $savePath
     * @return string
     */
    public static function exportCsv(string $filename, array $data, string $savePath): string
    {
        $string = "";
        foreach ($data as $value) {
            foreach ($value as $k => $val) {
                unset($val);
                $value[$k] = iconv('utf-8', 'gb2312', $value[$k]);
            }
            $string .= implode(",", $value) . "\n"; // 用英文逗号分开
        }
        // \header("Content-Type: text/csv; name='csv'");
        // \header("Content-type: application/octet-stream");
        // \header("Content-Disposition: attachment; filename=" . $filename);
        // \header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        // \header("Pragma: no-cache");
        // \header("Expires: 0");
        // 需要服务器端生成文件
        // $url = "/uploads/exportCsv/" . $filename;
        $url = $savePath . $filename;
        file_put_contents(APP_PATH . "public" . $url, $string);
        return $url;
    }

    /**
     * 4舍6入 5成双
     * @param $num
     * @param int $precision
     * @return float|int
     */
    public static function fmtMoney($num, int $precision = 2)
    {
        $pow = pow(10, $precision);
        // 判断不进1的情况（五后为0且为奇）
        if (substr($num * $pow, strpos($num * $pow, '.') + 1) == 5 && substr($num * $pow, strpos($num * $pow, '.') - 1, 1) % 2 == 0) {
            return floor($num * $pow) / $pow;
        } else {
            return round($num, $precision);
        }
        // return \substr(\sprintf('%0.3f', $amount), 0, - 1);
    }

    /**
     *  4舍五入
     * @param $amount
     * @return string
     */
    public static function fmtMoney2($amount): string
    {
        return sprintf('%0.2f', $amount);
    }

    /**
     * 异步http请求
     * @param $url
     * @param array $params
     */
    public static function asyncPost($url, array $params = [])
    {
        $error_code = "";
        $error_msg = "";
        $args = parse_url($url); // 对url做下简单处理
        $scheme = $args['scheme']; // 协议
        $host = $args['host']; // 获取上报域名
        $path = $args['path']; // 获取上报地址
        if ($scheme == 'http') {
            $fp = fsockopen($host, 80, $error_code, $error_msg, 20);
        } else {
            $fp = fsockopen("ssl://" . $host, 443, $error_code, $error_msg, 20);
        }
        if ($fp) {
            $data = http_build_query($params);
            stream_set_blocking($fp, true); // 开启了手册上说的非阻塞模式
            // stream_set_timeout($fp, 1); // 设置超时
            $header = "POST $path HTTP/1.1\r\n"; // 注意 GET/POST请求都行 我们需要自己按照要求拼装Header http协议遵循1.1
            $header .= "Host: $host\r\n";
            $header .= "Content-type:application/x-www-form-urlencoded\r\n";
            $header .= "Content-length:" . strlen($data) . "\r\n";
            $header .= "Connection: close\r\n\r\n"; // 长连接关闭
            $header .= "$data";
            fputs($fp, $header);
            fclose($fp);
        }
    }

    /**
     * 生成6位数
     * @param $num
     * @return string
     */
    public static function createSn($num): string
    {
        return substr(base_convert(md5(uniqid(md5(microtime(true)), true)), 16, 10), 0, $num);
    }

    /**
     * 先输出json，后台执行
     * @param $arr
     */
    public static function outputJsonEx($arr)
    {
        echo self::json($arr);
        fastcgi_finish_request();
    }

    /**
     * json转换为中文
     * @param $arr
     * @return false|string
     */
    public static function json($arr)
    {
        return json_encode($arr, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 密码加密
     * @param $pass
     * @return string
     */
    public static function password($pass): string
    {
        $key = 'www.lizhongwen.com';
        return md5($pass . $key);
    }

    /**
     * 输出json
     */
    public static function outputJson($arr)
    {
        header("Content-Type:application/json; charset=utf-8");
        exit(self::json($arr));
    }

    /**
     * 记录日志
     * @param $result
     * @param string $dir
     * @param string $format
     */
    public static function saveLogs($result, string $dir = "", string $format = "Ymd")
    {
        $logsData = [
            'time' => date("Y-m-d H:i:s"),
            'uri' => $_SERVER['REQUEST_URI'],
            'ip' => Lib::realIp(),
            'controller' => Filter::$controllerPath,
            'body' => Request::json(),
            'header' => Request::getAllHeaders(),
            'post' => $_POST,
            'get' => $_GET,
            'result' => $result
        ];
        Lib::logs($logsData, $dir, $format);
    }

    public static function logs($logsData, $dir = '', $format = "Ymd"): bool
    {
        if (!empty($dir)) {
            $dirLast = substr($dir, -1);
            if ($dirLast != "/") {
                $dir = $dir . "/";
            }
        }
        $path = APP_PATH . "resources/data/logs/$dir";
        if (!file_exists($path) || !is_writable($path)) {
            if (mkdir($path, 0755, true)) {
                file_put_contents(APP_PATH . "resources/data/logs/$dir" . date($format) . ".log", json_encode($logsData, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND);
                return true;
            } else {
                return false;
            }
        } else {
            file_put_contents(APP_PATH . "resources/data/logs/$dir" . date($format) . ".log", json_encode($logsData, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND);
            return true;
        }
    }

    /*
     * 创建订单号
     */
    public static function createOrderNo($pre = ''): string
    {
        $yCode = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
        $year = $yCode[intval(date("Y")) - 2020];
        $suffix = substr(base_convert(md5(uniqid(md5(microtime(true)), true)), 16, 10), 0, 6);
        list ($use_c, $sec) = explode(" ", microtime());
        $time = sprintf('%.0f', ((float)$use_c + (float)$sec) * 1000);
        $dateArr = explode(".", $time / 1000);
        $use_c = $dateArr[0];
        $sec = $dateArr[1] ?? 0;
        $date = date("mdHisx", $use_c);
        $dateMs = str_replace('x', $sec, $date);
        // $salt1 = \chr(rand(65, 90));
        $orderNo = $pre . $year . $dateMs . $suffix;
        $salt1 = "";
        $len = strlen($orderNo);
        if ($len < 20) {
            $salt1 = self::createSn(20 - $len);
        }
        return $orderNo . $salt1;

    }

    /**
     * 序列号生成器 (32位)
     */
    public static function serialGen($prefix = "", $salt = '')
    {
        $prefixLens = strlen($prefix);
        if ($prefixLens >= 32) {
            return substr($prefix, 0, 32);
        }
        $REMOTE_PORT = $_SERVER['REMOTE_PORT'] ?? '';
        $REQUEST_TIME_FLOAT = $_SERVER['REQUEST_TIME_FLOAT'] ?? '';
        $REMOTE_ADDR = $_SERVER['REMOTE_ADDR'] ?? '';
        $s = $REMOTE_PORT . $REQUEST_TIME_FLOAT . $REMOTE_ADDR;
        $s = md5(uniqid($prefix, true) . $s . $salt);
        $s = strtoupper($s);
        if (0 == $prefixLens) {
            return $s;
        }
        $result = str_split($s);
        $datum = [];
        for ($i = 0; $i < $prefixLens; $i++) {
            $pos = rand(1, 31);
            if (in_array($pos, $datum)) {
                $i = $i - 1;
                continue;
            }
            unset($result[$pos]);
            $datum[] = $pos;
        }
        $prefix = strtoupper($prefix);
        return $prefix . implode('', $result);
    }

    public static function isIdNo($id): bool
    {
        $id = strtoupper($id);
        $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
        $arr_split = array();
        if (!preg_match($regx, $id)) {
            return false;
        }
        if (15 == strlen($id)) // 检查15位
        {
            $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";

            @preg_match($regx, $id, $arr_split);
            // 检查生日日期是否正确
            $dtm_birth = "19" . $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
            if (!strtotime($dtm_birth)) {
                return false;
            } else {
                return true;
            }
        } else // 检查18位
        {
            $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
            @preg_match($regx, $id, $arr_split);
            $dtm_birth = $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
            if (!strtotime($dtm_birth)) // 检查生日日期是否正确
            {
                return false;
            } else {
                // 检验18位身份证的校验码是否正确。
                // 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
                $arr_int = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
                $arr_ch = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
                $sign = 0;
                for ($i = 0; $i < 17; $i++) {
                    $b = (int)$id[$i];
                    $w = $arr_int[$i];
                    $sign += $b * $w;
                }
                $n = $sign % 11;
                $val_num = $arr_ch[$n];
                if ($val_num != substr($id, 17, 1)) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    public static function httpPost($url, $postData, $upload = false)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36');
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_NOBODY, true);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, true);
        if ($upload) {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
        } else {
            curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($postData));
        }
        $output = curl_exec($curl);
        curl_close($curl);
        return json_decode($output, true);
    }

    public static function httpPostOut($url, $curlPost, $upload = false)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36');
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 10);
        curl_setopt($curl, CURLOPT_NOBODY, true);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, true);
        if ($upload) {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $curlPost);
        } else {
            curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($curlPost));
        }
        $return = curl_exec($curl);
        curl_close($curl);
        return $return;
    }

    public static function debug($var, $exit = false)
    {
        if (extension_loaded('xdebug')) {
            $class = xdebug_call_class();
            $func = xdebug_call_function();
            $file = xdebug_call_file();
            if ($class) {
                echo '# ' . $class . '::' . $func . ' | line: ' . xdebug_call_line();
            } else {
                echo $file;
            }
        }
        if (strstr($_SERVER['HTTP_USER_AGENT'], 'Mozilla')) {
            echo '<br />';
            var_dump($var);
            echo '<br />';
        } else {
            echo PHP_EOL;
            print_r($var);
            echo PHP_EOL;
        }
        if ($exit) {
            exit();
        }
    }

    public static function isSafeIp($ips, $currentIp = ''): bool
    {
        // 如ip为0.0.0.0 代表不限制
        if ($ips == '0.0.0.0' || empty($ips)) {
            return true;
        } else {
            if (empty($currentIp)) {
                $currentIp = self::realIp(); // 获取客户端IP
            }
            if (is_string($ips)) { // ip用"," 例如白名单IP：192.168.1.13,123.23.23.44,193.134.*.*
                $ips = explode(",", $ips);
            }
            if (in_array($currentIp, $ips)) {
                return true;
            }
            $ipRegexp = implode('|', str_replace(['*', '.'], ['\d+', '\.'], $ips));
            $rs = preg_match("/^(" . $ipRegexp . ")$/", $currentIp);
            if ($rs) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 获取浏览器类型
     * @return string
     */
    public static function getBrowser(): string
    {
        if (empty($_SERVER['HTTP_USER_AGENT'])) {
            return 'robot！';
        }
        if ((false == strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== FALSE)) {
            return 'Internet Explorer 11.0';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 10.0')) {
            return 'Internet Explorer 10.0';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 9.0')) {
            return 'Internet Explorer 9.0';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 8.0')) {
            return 'Internet Explorer 8.0';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 7.0')) {
            return 'Internet Explorer 7.0';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6.0')) {
            return 'Internet Explorer 6.0';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Edge')) {
            return 'Edge';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')) {
            return 'Firefox';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome')) {
            return 'Chrome';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
            return 'Safari';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
            return 'Opera';
        }
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], '360SE')) {
            return '360SE';
        }
        // 微信浏览器
        if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessage')) {
            return 'MicroMessage';
        }
        return $_SERVER['HTTP_USER_AGENT'];
    }


    /**
     * 获得用户的真实IP地址
     */
    public static function realIp()
    {
        $realIp = NULL;
        if (isset($_SERVER)) {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
                /* 取X-Forwarded-For中第一个非unknown的有效IP字符串 */
                foreach ($arr as $ip) {
                    $ip = trim($ip);
                    if ($ip != 'unknown') {
                        $realIp = $ip;
                        break;
                    }
                }
            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                $realIp = $_SERVER['HTTP_CLIENT_IP'];
            } else {
                if (isset($_SERVER['REMOTE_ADDR'])) {
                    $realIp = $_SERVER['REMOTE_ADDR'];
                } else {
                    $realIp = '0.0.0.0';
                }
            }
        } else {
            if (getenv('HTTP_X_FORWARDED_FOR')) {
                $realIp = getenv('HTTP_X_FORWARDED_FOR');
            } elseif (getenv('HTTP_CLIENT_IP')) {
                $realIp = getenv('HTTP_CLIENT_IP');
            } else {
                $realIp = getenv('REMOTE_ADDR');
            }
        }
        $onlineIp = [];
        preg_match("/[\d.]{7,15}/", $realIp, $onlineIp);
        return !empty($onlineIp[0]) ? $onlineIp[0] : '0.0.0.0';
    }

    /**
     * 获取服务器的ip
     */
    public static function realServerIp()
    {
        static $serverip = NULL;
        if ($serverip !== NULL) {
            return $serverip;
        }
        if (isset($_SERVER)) {
            if (isset($_SERVER['SERVER_ADDR'])) {
                $serverip = $_SERVER['SERVER_ADDR'];
            } else {
                $serverip = '0.0.0.0';
            }
        } else {
            $serverip = getenv('SERVER_ADDR');
        }
        return $serverip;
    }

    public static function saveFileCsv($url, $save_dir = '', $filename = '', $type = 0)
    {
        if (trim($url) == '') {
            return false;
        }
        if (trim($save_dir) == '') {
            $save_dir = './';
        }
        if (0 !== strrpos($save_dir, '/')) {
            $save_dir .= '/';
        }
        // 创建保存目录
        if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) {
            return false;
        }
        // 获取远程文件所采用的方法
        if ($type) {
            $ch = curl_init();
            $timeout = 5;
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
            $content = curl_exec($ch);
            curl_close($ch);
        } else {
            ob_start();
            readfile($url);
            $content = ob_get_contents();
            ob_end_clean();
        }
        // echo $content;
        $size = strlen($content);
        // 文件大小
        $fp2 = @fopen($save_dir . $filename, 'a');
        fwrite($fp2, $content);
        fclose($fp2);
        unset($content, $url);
        return array(
            'fileName' => $filename,
            'savePath' => $save_dir . $filename,
            'fileSize' => $size
        );
    }

    public static function isMobile(): bool
    {
        // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
        if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
            return true;
        }
        // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
        if (isset($_SERVER['HTTP_VIA'])) {
            // 找不到为false,否则为true
            return (bool)stristr($_SERVER['HTTP_VIA'], "wap");
        }
        // 脑残法，判断手机发送的客户端标志,兼容性有待提高
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $clientKeywords = array(
                'nokia',
                'sony',
                'ericsson',
                'mot',
                'samsung',
                'htc',
                'sgh',
                'lg',
                'sharp',
                'sie-',
                'philips',
                'panasonic',
                'alcatel',
                'lenovo',
                'iphone',
                'ipod',
                'blackberry',
                'meizu',
                'android',
                'netfront',
                'symbian',
                'ucweb',
                'windowsce',
                'palm',
                'operamini',
                'operamobi',
                'openwave',
                'nexusone',
                'cldc',
                'midp',
                'wap',
                'mobile'
            );
            // 从HTTP_USER_AGENT中查找手机浏览器的关键字
            if (preg_match("/(" . implode('|', $clientKeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
                return true;
            }
        }
        // 协议法，因为有可能不准确，放到最后判断
        if (isset($_SERVER['HTTP_ACCEPT'])) {
            // 如果只支持wml并且不支持html那一定是移动设备
            // 如果支持wml和html但是wml在html之前则是移动设备
            if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
                return true;
            }
        }
        return false;
    }

    public static function subStr($str, $start = 0, $length = 0, $append = true)
    {
        $str = trim($str);
        $strLength = strlen($str);
        if ($length == 0 || $length >= $strLength) {
            return $str;
        } elseif ($length < 0) {
            $length = $strLength + $length;
            if ($length < 0) {
                $length = $strLength;
            }
        }
        if (function_exists('mb_substr')) {
            $newStr = mb_substr($str, $start, $length, 'utf-8');
        } elseif (function_exists('iconv_substr')) {
            $newStr = iconv_substr($str, $start, $length, 'utf-8');
        } else {
            // $newStr = trim_right(substr($str, 0, $length));
            $newStr = substr($str, $start, $length);
        }
        if ($append && $str != $newStr) {
            $newStr .= '...';
        }
        return $newStr;
    }

    /**
     * 加密函数
     *
     * @param $txt
     * @param string $key 密钥
     * @return string 加密后的字符串
     */
    public static function encrypt($txt, string $key = 'www.xxx.common'): string
    {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $nh = rand(0, 64);
        $ch = $chars[$nh];
        $mdKey = md5($key . $ch);
        $mdKey = substr($mdKey, $nh % 8, $nh % 8 + 7);
        $txt = base64_encode($txt);
        $tmp = '';
        $k = 0;
        for ($i = 0; $i < strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = ($nh + strpos($chars, $txt[$i]) + ord($mdKey[$k++])) % 64;
            $tmp .= $chars[$j];
        }
        return urlencode($ch . $tmp);
    }

    /**
     * 解密函数
     *
     * @param $txt
     * @param string $key 密钥
     * @return string 加密前的字符串
     */
    public static function decrypt($txt, string $key = 'www.xxx.common')
    {
        if (empty($txt)) {
            return true;
        }
        $txt = urldecode($txt);
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $ch = $txt[0];
        $nh = strpos($chars, $ch);
        $mdKey = md5($key . $ch);
        $mdKey = substr($mdKey, $nh % 8, $nh % 8 + 7);
        $txt = substr($txt, 1);
        $tmp = '';
        $k = 0;
        for ($i = 0; $i < strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = strpos($chars, $txt[$i]) - $nh - ord($mdKey[$k++]);
            while ($j < 0)
                $j += 64;
            $tmp .= $chars[$j];
        }
        return base64_decode($tmp);
    }

    /**
     * @param string $str
     * @param int $start
     * @param int $length
     * @param string $replacement
     * @return string
     */
    public static function strReplace(string $str, int $start = 1, int $length = 3, string $replacement = '*'): string
    {
        $len = mb_strlen($str, 'utf-8');
        if ($len > $start + $length) {
            $str1 = mb_substr($str, 0, $start, 'utf-8');
            $str2 = mb_substr($str, $start + $length, NULL, 'utf-8');
        } else {
            $str1 = mb_substr($str, 0, 1, 'utf-8');
            $str2 = mb_substr($str, $len - 1, 1, 'utf-8');
            $length = $len - 2;
        }
        $newStr = $str1;
        $newStr .= str_repeat($replacement, $length);
        $newStr .= $str2;
        return $newStr;
    }

    /**
     * 只保留字符串首尾字符，隐藏中间用*代替（两个字符时只显示第一个）
     *
     * @param string $userName
     * @return string 格式化后的姓名
     */
    public static function formatName(string $userName): string
    {
        $strlen = mb_strlen($userName, 'utf-8');
        $firstStr = mb_substr($userName, 0, 1, 'utf-8');
        $lastStr = mb_substr($userName, -1, 1, 'utf-8');
        return $strlen == 2 ? $firstStr . str_repeat('*', mb_strlen($userName, 'utf-8') - 1) : $firstStr . str_repeat("*", $strlen - 2) . $lastStr;
    }

    /**
     * 隐藏卡号，只显示前4位和后4位
     *
     * @param string $bankCardNo
     * @return string
     */
    public static function formatBankCardNo(string $bankCardNo): string
    {
        // 截取银行卡号前4位
        $prefix = substr($bankCardNo, 0, 4);
        // 截取银行卡号后4位
        $suffix = substr($bankCardNo, -4, 4);
        return $prefix . " **** **** **** " . $suffix;
    }

    public static function randStr($len = 8): string
    {
        return strtolower(substr(self::serialGen(""), 0, $len));
    }

    public static function xmlToArray($xml, $is_file = false)
    {
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        if ($is_file) {
            if (!file_exists($xml))
                return false;
            $xml_str = file_get_contents($xml);
        } else {
            $xml_str = $xml;
        }
        return json_decode(json_encode(simplexml_load_string($xml_str, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    public static function isAliClient(): bool
    {
        return strpos($_SERVER['HTTP_USER_AGENT'], 'Alipay') !== false;
    }

    public static function fileExtension($filename): string
    {
        $path_info = pathinfo($filename);
        return strtolower($path_info['extension']);
    }

    public static function delAllByDir($path)
    {
        if (is_dir($path) && file_exists($path)) {
            $handle = opendir($path);
            //$file=readdir($handle);        为什么不能在外面读取目录句柄
            while (false !== ($file = readdir($handle))) {
                if ($file == '.' || $file == '..') {
                    continue;
                }
                if (is_dir($path . '/' . $file)) {
                    self::delAllByDir($path . '/' . $file);
                } else {
                    unlink($path . '/' . $file);    //删除文件
                }
            }
            closedir($handle);      //关闭目录句柄
            rmdir($path);           //删除空目录
        }
    }

}